#include "stdafx.h"
#include "ScriptValidator.h"

namespace Testing
{
   namespace Scripts
   {
   
      // -------------------------------- CONSTRUCTION --------------------------------

      // ------------------------------- STATIC METHODS -------------------------------
      
      // ------------------------------- PUBLIC METHODS -------------------------------
      
      /// <summary>Perform textual comparison of a script and it's validation copy</summary>
      /// <param name="in">original script</param>
      /// <param name="out">compiled copy</param>
      bool  ScriptTextValidator::Compare(const LineArray& in, const LineArray& out)
      {
         // Variables/Arguments count
         if (in.size() != out.size())
            throw TextMismatch(HERE, L"command count", VString(L"%d", in.size()), VString(L"%d", out.size()));

         else 
         {
            UINT line = 1;
            for (auto c1 = in.begin(), c2 = out.begin(); c1 != in.end(); ++c1, ++c2)
            {
               // Skip comparison of comments
               /*if (!c1->empty() && !c2->empty() && c1->front() == '*' && c2->front() == '*')
                  continue;*/
               // Compare command text
               if (*c1 != *c2)
                  throw TextMismatch(HERE, VString(L"(line %d) command text", line), *c1, *c2);
            
               ++line;
            }
         }

         return true;
      }

      /// <summary>Perform comparison of commands generated by compiler between script and it's validation copy</summary>
      /// <param name="in">original script</param>
      /// <param name="out">compiled copy</param>
      bool  ScriptTextValidator::Compare(const ScriptFile& in, const ScriptFile& out)
      {
         // Properties
         if (in.Name != out.Name)
            throw TextMismatch(HERE, L"script name", in.Name, out.Name);
         if (in.Description != out.Description)
            throw TextMismatch(HERE, L"script description", in.Description, out.Description);
         if (in.Version != out.Version)
            throw TextMismatch(HERE, L"script version", in.Version, out.Version);
         if (in.Game != out.Game)
            throw TextMismatch(HERE, L"script game version", VersionString(in.Game), VersionString(out.Game));
         if (in.CommandName != out.CommandName)
            throw TextMismatch(HERE, L"script command ID", in.CommandName, out.CommandName);

         // Variables/Arguments count
         if (in.Variables.Count != out.Variables.Count)
            throw TextMismatch(HERE, L"script variable count", in.Variables.Count, out.Variables.Count);

         // Variables/Arguments
         else for (auto v1 = in.Variables.begin(), v2 = out.Variables.begin(); v1 != in.Variables.end(); ++v1, ++v2)
            Compare(*v1, *v2);

         // Command count
         if (in.Commands.Input.size() != out.Commands.Input.size())
            throw TextMismatch(HERE, L"script command count", in.Commands.Input.size(), out.Commands.Input.size());
         else 
         {
            UINT line = 1;
            // Commands
            for (auto c1 = in.Commands.Input.begin(), c2 = out.Commands.Input.begin(); c1 != in.Commands.Input.end(); ++c1, ++c2)
            {
               Compare(line, *c1, *c2);
               ++line;
            }
         }

         return true;
      }

      // ------------------------------ PROTECTED METHODS -----------------------------

      // ------------------------------- PRIVATE METHODS ------------------------------
      

      /// <summary>Perform textual comparison two script variables</summary>
      /// <param name="in">original variable</param>
      /// <param name="out">compiled copy</param>
      bool  ScriptTextValidator::Compare(const ScriptVariable& in, const ScriptVariable& out)
      {
         try
         {
            if (in.Name != out.Name)
               throw TextMismatch(HERE, VString(L"Arg/Var Name (id=%d)", in.ID), in.Name, out.Name);
            if (in.ID != out.ID)
               throw TextMismatch(HERE, VString(L"Arg/Var ID '%s'", in.Name.c_str()), in.ID, out.ID);
            if (in.Type != out.Type)
               throw TextMismatch(HERE, VString(L"Arg/Var type flag of '%s'", in.Name.c_str()), GetString(in.Type), GetString(out.Type));

            if (in.Description != out.Description)
               throw TextMismatch(HERE, VString(L"Argument description '%s'", in.Name.c_str()), in.Description, out.Description);
            if (in.ParamType != out.ParamType)
               throw TextMismatch(HERE, VString(L"Argument Type '%s'", in.Name.c_str()), GetString(in.ParamType), GetString(out.ParamType));
         }
         catch (ExceptionBase&)
         {
            Console << "Orig: " << in << ENDL;
            Console << "Copy: " << out << ENDL;
            throw;
         }

         return true;
      }

      /// <summary>Perform textual comparison of two script commands</summary>
      /// <param name="line">1-based line number</param>
      /// <param name="in">original commands</param>
      /// <param name="out">compiled copy</param>
      bool  ScriptTextValidator::Compare(UINT line, const ScriptCommand& in, const ScriptCommand& out)
      {
         // Syntax + Text
         if (in.Syntax != out.Syntax)
            throw TextMismatch(HERE, VString(L"(line %d) command syntax", line), in.Syntax.Text, out.Syntax.Text);
         if (in.Text != out.Text)
            throw TextMismatch(HERE, VString(L"(line %d) command text", line), in.Text, out.Text);

         // Parameter count
         if (in.Parameters.size() != out.Parameters.size())
            throw TextMismatch(HERE, VString(L"(line %d) parameter count", line), in.Parameters.size(), out.Parameters.size());
         else 
         {
            UINT param = 1;

            // Parameters
            for (auto p1 = in.Parameters.begin(), p2 = out.Parameters.begin(); p1 != in.Parameters.end(); ++p1, ++p2)
            {
               Compare(line, param, *p1, *p2);
               ++param;
            }
         }
         
         return true;
      }

      /// <summary>Perform textual comparison of two script parameters</summary>
      /// <param name="line">1-based line number</param>
      /// <param name="param">1-based parameter index</param>
      /// <param name="in">original parameter</param>
      /// <param name="out">compiled copy</param>
      bool  ScriptTextValidator::Compare(UINT line, UINT param, const ScriptParameter& in, const ScriptParameter& out)
      {
         try
         {
            // Type
            if (in.Syntax.Type != out.Syntax.Type)
               throw TextMismatch(HERE, VString(L"(line %d, param %d) syntax", line, param), GetString(in.Syntax.Type), GetString(out.Syntax.Type));

            // Text
            if (in.Text != out.Text)
               throw TextMismatch(HERE, VString(L"(line %d, param %d) text", line, param), in.Text, out.Text);

            // Value
            if (in.Value.Type != out.Value.Type)
               throw TextMismatch(HERE, VString(L"(line %d, param %d) value type", line, param), GetString(in.Value.Type), GetString(out.Value.Type));

            else if (in.Value.Type == ValueType::Int && in.Value.Int != out.Value.Int)
               throw TextMismatch(HERE, VString(L"(line %d, param %d) value", line, param), in.Value.Int, out.Value.Int);

            else if (in.Value.Type == ValueType::String && in.Value.String != out.Value.String)
               throw TextMismatch(HERE, VString(L"(line %d, param %d) value", line, param), in.Value.String, out.Value.String);
         }
         catch (ExceptionBase&)
         {
            Console << "Orig: " << in << ENDL;
            Console << "Copy: " << out << ENDL;
            throw;
         }

         return true;
      }

      /// <summary>Create text mismatch exception</summary>
      /// <param name="src">throw source.</param>
      /// <param name="prop">name of property that mismatches</param>
      /// <param name="a">original</param>
      /// <param name="b">copy</param>
      /// <returns></returns>
      ValidationException  ScriptTextValidator::TextMismatch(const GuiString& src, const GuiString& prop, const GuiString& a, const GuiString& b)
      {
         return ValidationException(src, VString(L"text mismatch: %s", prop.c_str()), a, b);
      }
      
      /// <summary>Create text mismatch exception</summary>
      /// <param name="src">throw source.</param>
      /// <param name="prop">name of property that mismatches</param>
      /// <param name="a">original</param>
      /// <param name="b">copy</param>
      /// <returns></returns>
      ValidationException  ScriptTextValidator::TextMismatch(const GuiString& src, const GuiString& prop, int a, int b)
      {
         return ValidationException(src, VString(L"text mismatch: %s", prop.c_str()), VString(L"%d",a), VString(L"%d",b));
      }
   }
}

